﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Mitsubishi.Base;

namespace Mitsubishi
{
    public abstract class MCBase : IDisposable
    {
        /*
         读
             - 成批读出：字、位       ：指定一个地址 D0   指定一个数量     返回相关数据   连续请求数据     
             - 字单位的随机读出       ：不连续的地址请求     D0      D100     W10        每个地址读一个长度
             - 多块成批读出           ：不连续的地址            地址带长度          位按字处理？

         写
             - 成批写入：字、位
             - 随机写入：字、位
             - 多块成批写入
       */

        public virtual void Connect(string Ip,int port,int timeout = 3000) { }

        public virtual byte[] Read(Area area, string address, ushort count, RequestType requestType = RequestType.WORD, Action<object> callback = null) { return null; }

        /// <summary>
        /// 成批写入
        /// </summary>
        /// <param name="area"></param>
        /// <param name="address"></param>
        /// <param name="datas">传入数据 (小端)  0x01 => true,0x00 => false </param>
        /// <param name="requestType"></param>
        public virtual void Write(Area area, string address, byte[] datas, RequestType requestType = RequestType.WORD, Action<object> callback = null) { }

        public byte[] Read(string address,ushort count = 1, RequestType requestType = RequestType.WORD,Action<object> callback = null)
        {
            Area area = AddressParse(address.Substring(0, 1));
            return this.Read(area,address,count, requestType,callback);
        }

        // 1401(字、位)
        /// <summary>
        /// 成批写入
        /// </summary>
        /// <param name="area"></param>
        /// <param name="address"></param>
        /// <param name="datas">传入数据 (小端) true 0x01,false 0x00</param>
        /// <param name="requestType"></param>
        public void Write(string address, byte[] datas, RequestType requestType = RequestType.WORD, Action<object> callback = null)
        {
            Area area = AddressParse(address.Substring(0, 1));
            this.Write(area,address,datas,requestType,callback);
        }

        protected Area AddressParse(string addrChar)
        {
            Area area;
            switch (addrChar)
            {
                case "X":
                    area = Area.X; 
                    break;
                case "Y": area = Area.Y;
                    break;
                case "M":
                    area = Area.M;
                    break;
                case "D":
                    area = Area.D;
                    break;
                default:
                    throw new Exception("暂不支持该地址类型通过字符串地址的方式进行访问");
            };
            return area;
        }
        public int FindFirstDigitPosition(string input)
        {
            // 使用 LINQ 查找第一个数字字符的索引
            var digitChar = input.FirstOrDefault(c => char.IsDigit(c));
            return digitChar != default(char) ? input.IndexOf(digitChar) : -1;
        }

        protected byte[] GetAddress(string address, Area area)
        {
            int idx = FindFirstDigitPosition(address);
            address = idx == -1 ? address : address.Substring(idx);
            int addr = int.Parse(address);
            List<byte> addr_bytes = new List<byte>();
            List<Area> areas = new List<Area>() { Area.X, Area.Y };
            if (areas.Contains(area))
            {
                string addr_str = address.PadLeft(6, '0');
                byte[] hex_bytes = Convert.FromHexString(addr_str);
                Array.Reverse(hex_bytes);
                addr_bytes.AddRange(hex_bytes.ToArray());
            }
            else
            {
                addr_bytes.Add((byte)(addr % 256));
                addr_bytes.Add((byte)(addr / 256 % 256));
                addr_bytes.Add((byte)(addr / 256 / 256 % 256));
            }

            return addr_bytes.ToArray();
        }

        protected List<T> GetData<T>(byte[] resp_bytes)
        {
            List<T> data = new List<T>();
            if (typeof(T) == typeof(bool))
            {
                for (int i = 0; i < resp_bytes.Length; i++)
                {
                    byte b = resp_bytes[i];
                    dynamic f1 = (b & 0x10) != 0 ? true : false;
                    dynamic f2 = (b & 0x01) != 0 ? true : false;
                    data.Add(f1);
                    data.Add(f2);
                }
            }
            else
            {
                int size = Marshal.SizeOf(typeof(T));
                Type bitConverter = typeof(BitConverter);
                System.Reflection.MethodInfo? method =
                    bitConverter.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
                    .FirstOrDefault(m => m.ReturnType == typeof(T) && m.GetParameters().Length == 2);
                if (method == null)
                    throw new Exception("Unsupported type Converter");
                List<byte> list = resp_bytes.ToList();
                for (int i = 0; i < list.Count; i += size)
                {
                    List<byte> tmp = list.GetRange(i, size);
                    if (!BitConverter.IsLittleEndian)
                        tmp.Reverse();
                    dynamic val = method.Invoke(bitConverter, new object[] { tmp.ToArray(), 0 });
                    data.Add(val);
                }
            }

            return data;
        }

        // 从数据到字节的转换
        protected byte[] GetBytes<T>(params T[] values)
        {
            List<byte> bytes = new List<byte>();
            if (typeof(T) == typeof(bool))
            {
                foreach (var v in values)
                {
                    bytes.Add((byte)(bool.Parse(v.ToString()) ? 0x01 : 0x00));
                }
            }
            else
            {
                foreach (var v in values)
                {
                    dynamic d = v;
                    byte[] v_bytes = BitConverter.GetBytes(d);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(v_bytes);
                    }
                    bytes.AddRange(v_bytes);
                }

            }

            return bytes.ToArray();
        }

        public virtual byte[] SendAndReceive(byte[] req_bytes)
        {
            return null;
        }

        public virtual void CheckResponse(byte[] resp_bytes) { }

        public virtual void Disconnect() { }

        public void Dispose()
        {
            this.Disconnect();
        }
    }
}
